#include "../include/kc.h"
#include "../include/kasm.h"
#include "../include/mouse.h"
#include "../include/keyboard.h"

//el primer byte que envia el mouse es de control. los siguientes defines
//son para ser usados con el byte de control
#define LEFT_CLICK 0x01		//bit de click izquierdo
#define RIGHT_CLICK 0x02	//bit de click derecho
#define CONTROL_BIT 0x08	//bit de control
#define X_NEG 0x10			//bit de signo del valor x
#define Y_NEG 0x20			//bit de signo del valor y

//mouseBuffer mouseBuff = {0};

void fillMouseBuffer(videoBuff * vid, mouseBuffer * mouseBuff);

//Traduce los paquetes que envia el mouse, y realiza las acciones pertinentes
int
mouseTranslator(videoBuff * vid, buffer_t * stdin, mouseBuffer * mouseBuff, unsigned char code) {
	//vector que representa el paquete completo del mouse (3 bytes)
	static unsigned char codes[3];
	//cantidad de codes que tengo
	static int numCode = 0;

	int stdinWritten = 0;

	//posicion inicial del puntero
	static int x = 0, y = 0;

	//posicion de inicio de la zona seleccionada
	static int yIni = 0, xIni = 0;

	//variables que indican el estado del click izquierdo y derecho
	static char mouseLeftPressed = 0;
	static char mouseRightPressed = 0;

	//veo que si es el primer byte (el de control), sea valido
	if ( (code & CONTROL_BIT) != 0 || numCode != 0) {
		//agrego el codigo y aumento el indice
		codes[numCode++] = code;

		//si tengo un paquete entero (3 bytes), lo utilizo
		if(numCode == 3) {
			//chequeo si el y es negativo
			if ( codes[0] & Y_NEG )
				y -= 0xFFFFFF00 | codes[2]; //lo paso a int negativo
			else
				y -= codes[2];

			//chequeo si el x es negativo
			if ( codes[0] & X_NEG )
				x += 0xFFFFFF00 | codes[1]; //lo paso a int negativo
			else
				x += codes[1];

			//muevo el puntero de acuerdo a los nuevos valores de x e y
			//le paso referencias porque puede ser que x e y se vayan de la
			//pantalla, en cuyo caso la funcion me lo arregla
			screen_pointer_move(vid, &x, &y);

			//si el boton izquierdo del mouse es presionado
			if ( codes[0] & LEFT_CLICK ) {
				//si el mouse no habia estado presionado antes, quiere decir
				//que estoy empezando a seleccionar, asi que seteo el
				//x y el y iniciales
				if ( !mouseLeftPressed ) {
					mouseLeftPressed = 1;
					xIni = x;
					yIni = y;
				}

				//pido a la video que pinte la zona comprendida entre los
				//4 parametros que se le pasa a la funcion
				screen_paint(vid, xIni, yIni, x, y);
			}
			else {
				//si el click izquierdo habia estado presionado y fue soltado
				if (mouseLeftPressed) {
					//lleno el buffer del mouse con lo seleccionado
					fillMouseBuffer(vid, mouseBuff);
					mouseLeftPressed = 0;
				}
			}

			//si el boton derecho del mouse es presionado
			if ( codes[0] & RIGHT_CLICK ) {

				//voy a mandar una sola vez el click derecho del mouse,
				//hasta que lo suelten y vuelvan a presionar
				if (mouseRightPressed != 1) {
					mouseRightPressed = 1;
					if (mouseBuff->cant != 0) {
						putbuff(stdin, mouseBuff->buffer, mouseBuff->cant);
						stdinWritten = 1; //escribieron algo, asi que lo seteo
					}
				}
			}
			else {
				//si el click derecho no fue presionado, lo seteo en 0
				mouseRightPressed = 0;
			}

			//reseteo el numero de codes del mouse
			numCode = 0;
		}
	}
	return stdinWritten;
}

void fillMouseBuffer(videoBuff * vid, mouseBuffer * mouseBuff) {
	int i;
	//zona pintada por el mouse, que me devuelve la video
	paintedSectionT paintedSection = screen_get_selected_coords(vid);

	//si no hay zona pintada, salgo sin tocar el buffer
	if (!paintedSection.isPainted)
		return;

	//reseteo el tamano del buffer
		mouseBuff->cant = 0;

	//voy buscando de a lineas la zona pintada
	//para ir agregando el '\n'
	for ( i = paintedSection.yIni; i <= paintedSection.yFin; i++){
		mouseBuff->cant += screen_get_painted_line(vid, &(mouseBuff->buffer[mouseBuff->cant]), i);
		if (mouseBuff->cant != 0)
			mouseBuff->buffer[mouseBuff->cant++] = '\n';
	}
	//si copie una sola linea, no deberia poner un '\n' al final
	if (paintedSection.yIni == paintedSection.yFin)
		mouseBuff->cant--;
}

/*---------------------------------------------------------------------
 Funciones de Inicializacion del mouse. Obtenidas de la pagina
 http://houbysoft.com/download/ps2mouse.html. Se agregaron comentarios
 a las lineas, se hicieron leves modificaciones a algunas funciones, y
 en la funcion initMouse se agregaron lineas de configuracion.
*/

//Espera a que el mouse este disponible, y le escribe.
void mouse_write(unsigned char a_write)
{
	//Espero a poder enviarle un comando
	writeWait();
	//Aviso que el proximo comando ira al teclado, y no al mouse
	_outport(0x64, 0xD4);
	//Espero devuelta a poder enviarle el comando
	writeWait();
	//Y le mando el comando
	_outport(0x60, a_write);
}

//Espera a que el mouse este disponible, y lee de su buffer.
unsigned char mouse_read()
{
	//Espero a poder leer, y leo del puerto 0x60 (buffer de mouse y teclado)
	readWait();
	return _inport(0x60);
}

//Configura el mouse y lo inicializa
void
initMouse(void) {

	//activar el puerto del mouse
	writeWait();
	_outport(0x64,0xA8);

	//activar la interrupcion: obtengo el compaq status byte
	writeWait();
	_outport(0x64,0x20);

	//y guardo el compaq status
	unsigned char status_byte;
	readWait();
	status_byte = (_inport(0x60) | 2);
	status_byte = status_byte & 0xDF;

	//le digo al controlador del mouse que voy a enviar un status byte
	writeWait();
	_outport(0x64, 0x60);

	//y le envio el byte
	writeWait();
	_outport(0x60, status_byte);

	mouse_read();		//espero a leer el ack

	//le digo al mouse que setee los valores default
	mouse_write(0xF6);
	mouse_read();

	//seteo el scaling en 2:1
	mouse_write(0xE7);
	mouse_read();

	//seteo la resolucion en la minima (para que
	// se mueva mas lento el puntero)
	mouse_write(0xE8);
	mouse_read();
	mouse_write(0x00);
	mouse_read();

	//activo el mouse en stream mode (para recibir paquetes)
	mouse_write(0xF4);
	mouse_read();

	return;
}
//-----------------------------------------------------------------------
